package interpreter

import (
	"errors"
	"fmt"
	"learning/gooop/behavioral_patterns/interpreter/tokens"
	"strings"
)

type tFieldExpression struct {
	op tokens.Tokens
	field string
	stringLiteral string
	intLiteral int
}


func newStringFieldExpression(op tokens.Tokens, field string, s string) *tFieldExpression {
	return &tFieldExpression{
		op, field, strings.Trim(s, "'"), 0,
	}
}

func newIntFieldExpression(op tokens.Tokens, field string, value int) *tFieldExpression {
	return &tFieldExpression{
		op, field, "", value,
	}
}

func (me *tFieldExpression) Eval(row IDataRow) bool {
	e, fld := row.GetField(me.field)
	if e != nil {
		panic(e)
	}

	switch fld.DataType() {
	case StringDataType:
		e,v := fld.GetString()
		if e != nil {
			panic(e)
		}
		return me.EvalString(v)

	case IntDataType:
		e,v := fld.GetInt()
		if e != nil {
			panic(e)
		}
		return me.EvalInt(v)

	default:
		panic(errors.New("unknown data type"))
	}
}

func (me *tFieldExpression) EvalString(value string) bool {
	switch me.op {
	case tokens.Equal:
		return value == me.stringLiteral

	case tokens.NotEqual:
		return value != me.stringLiteral

	case tokens.Like:
		fallthrough
	case tokens.NotLike:
		like := false
		p := strings.HasPrefix(value, "%")
		s := strings.HasPrefix(value, "%")

		if p && s {
			like = strings.Contains(value, me.stringLiteral)
		} else if p {
			like = strings.HasSuffix(value, me.stringLiteral)
		} else if s {
			like = strings.HasPrefix(value, me.stringLiteral)
		} else {
			like = value == me.stringLiteral
		}

		if me.op == tokens.Like {
			return like
		} else {
			return !like
		}
		break

	default:
		panic(errors.New(fmt.Sprintf("unsupported string operation: %s", me.op)))
	}

	return false
}


func (me *tFieldExpression) EvalInt(value int) bool {
	switch me.op {
	case tokens.Equal:
		return value == me.intLiteral

	case tokens.NotEqual:
		return value != me.intLiteral

	case tokens.Greater:
		return value > me.intLiteral

	case tokens.GreaterEqual:
		return value >= me.intLiteral

	case tokens.Less:
		return value < me.intLiteral

	case tokens.LessEqual:
		return value <= me.intLiteral

	default:
		panic(errors.New(fmt.Sprintf("unsupported int operation: %s", me.op)))
	}
}